/**
  * Author: Mei
  * Date: 2020-11-28 20:49
  * Description: 
  *
  */
#ifndef THREADPOOL_H
#define THREADPOOL_H

#include <functional>
#include <future>
#include <mutex>
#include <queue>
#include <utility>
#include <vector>
#include "SafeQueue.h"

class ThreadPool {
private:
    class ThreadWorker {
    private:
        int m_id;
        ThreadPool *m_pool;
    public:
        ThreadWorker(ThreadPool *pool, const int id) : m_pool(pool), m_id(id) {
        }
        //任务线程  主要处理任务队列
        void operator()() {
            std::function<void()> func;
            bool dequeued;
            while (!m_pool->shutdown()) {
                std::unique_lock<std::mutex> lock(m_pool->m_conditional_mutex); //锁住当前执行线程的互斥量
                if(m_pool->m_queue.empty()) { //如果任务队列为空  阻塞等待  直到有任务才去执行
                    m_pool->m_condition_lock.wait(lock);
                }
                dequeued = m_pool->m_queue.dequeue(func);
                if(dequeued) { //如果取出任务队列  直接执行
                    func();
                }
            }
        }
    };

    bool m_shutdown;
    SafeQueue<std::function<void()>> m_queue; //任务队列
    std::vector<std::thread> m_threads;  //执行的任务线程
    std::mutex m_conditional_mutex; //互斥量
    std::condition_variable m_condition_lock; //声明条件变量
public:
    ThreadPool(const int n_threads) : m_threads(std::vector<std::thread>(n_threads)), m_shutdown(false) {
    }

    ThreadPool(const ThreadPool &) = delete; //禁止构造该对象
    ThreadPool(ThreadPool &&) = delete; //禁止构造该对象

    ThreadPool &operator=(const ThreadPool &) = delete;

    ThreadPool &operator=(ThreadPool &&) = delete;

    //初始化线程池
    void init() {
        for (int i = 0; i < m_threads.size(); ++i) {
            //每次把当前的任务线程传递出去
            m_threads[i] = std::thread(ThreadWorker(this, i));
        }
    }

    //线程结束
    void shutdown() {
        m_shutdown = true;
        m_condition_lock.notify_all();
        for (int i = 0; i < m_threads.size(); ++i) {
            if (m_threads[i].joinable()) {
                m_threads[i].join();
            }
        }
    }

    //加入任务队列
    template<typename F, typename ...Args>
    auto submit(F &&f, Args &&... args) -> std::future<decltype(f(args...))> {
        //创建一个函数对象 并绑定参数
        std::function<decltype(f(args...))()> func = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
        //创建一个智能指针对象  对象是packaged_task类型 把执行函数放进去   后面只需要绑定future
        auto task_ptr = std::make_shared(std::packaged_task<decltype(f(args...))>(func));

        //定义一个函数对象  应该放到任务队列中
        std::function<void()> wrapper_func = [task_ptr]() {
            (*task_ptr)();
        };
        //加入队列
        m_queue.enqueue(wrapper_func);

        //当队列中有任务时 应该通知任务线程去执行
        m_condition_lock.notify_one();

        return task_ptr->get_future(); //绑定future
    };

};


#endif